#include "OutputWindow.h"

#include "server/DmxClient.h"
#include "MainWindow.h"

void DmxChannel::setMainValue(int value)
{
	m_mainValue = value;
	setValue(m_value);
}

void DmxChannel::setValue(int value)
{
	if(isHold())
		return;
	
	SimpleChannel::setValue(value);
	
	if(m_holdTransmit)
		return;
	
	//qDebug() << "DmxChannel::setValue: DMX#"<<dmxNum()<<" value:"<<value<<" effectiveValue:"<<effectiveValue();
	
	if(m_client)
		m_client->setDmxChannel(dmxNum(), effectiveValue());
	
	 
}

bool DmxChannel::ignoreProperty(const QString& propertyName, bool /*isLoading*/)
{
	return propertyName == "value";
}

int DmxChannel::effectiveValue()
{
	float floatVal = ((float)m_mainValue)/255.0;
	int effectiveValue = int(value() * floatVal);
	return effectiveValue;
}

IndicatorWidget::IndicatorWidget(DmxChannel *chan, QWidget *parent)
	: QWidget(parent)
{
	connect(chan, SIGNAL(valueChanged(int)), this, SLOT(valueChanged(int)));
	valueChanged(chan->value());
	setMinimumSize(QSize(24,24));
	setMaximumSize(QSize(24,24));
}

void IndicatorWidget::valueChanged(int value)
{
	int w = 24, h = 24;
	if(m_pixmap.width()  != w ||
	   m_pixmap.height() != h)
		m_pixmap = QPixmap(w,h);
	
	m_pixmap.fill(Qt::transparent);
	
	QPainter p(&m_pixmap);
	p.setPen(Qt::black);
	p.setBrush(QColor(0,value,0));
	p.drawRect(m_pixmap.rect().adjusted(0,0,-1,-1));
	
	update();
}

void IndicatorWidget::paintEvent(QPaintEvent  *)
{
	QPainter p(this);
	p.drawPixmap(0,0,m_pixmap);
}
	
OutputWindow::OutputWindow()
	: QWidget()
	, m_lockValueChanged(false)
	, m_holdTransmit(false)
{
	setWindowTitle("DMX Output Window");
	setWindowIcon(QIcon("/usr/share/icons/Bluecurve/32x32/apps/icon-mixer.png"));
	
	setupChannels();
	setupGui();
	
	connect(MainWindow::inst(), SIGNAL(mainLevelChanged(int)), this, SLOT(mainLevelChanged(int)));
}

void OutputWindow::setupChannels()
{
	GetSettingsObject();
	QVariantList channels = settings.value("dmxChannels").toList();
	
	DmxClient *client = MainWindow::inst()->dmxClient();
	
	for(int i=1; i<NUM_OUTPUT_FADERS; i++)
	{
		DmxChannel *chan;
		if(i-1 < channels.size())
		{
// 			qDebug() << "OutputWindow::setupChannels: Loading channel "<<i<<" from storage"; 
			QByteArray ba = channels[i-1].toByteArray();
			chan = new DmxChannel(client);
			chan->fromByteArray(ba);
// 			if(chan->dmxNum() != i)
// 				chan->setDmxNum(i);
		}
		else
		{
// 			qDebug() << "OutputWindow::setupChannels: Creating new channel object for channel "<<i;
			chan = new DmxChannel(client, "", i, i);
		}
			
		m_channelList.append(chan);
		connect(chan, SIGNAL(valueChanged(int)), this, SLOT(dmxChannelValueChanged(int)));
	}
}

void OutputWindow::storeChannels()
{
	QVariantList list;
	foreach(DmxChannel *chan, m_channelList)
		list << chan->toByteArray();
	GetSettingsObject();
	settings.setValue("dmxChannels", list);
}


void OutputWindow::setupGui()
{
	QVBoxLayout *vbox = new QVBoxLayout(this);
	
	m_sliderGrid = new QGridLayout();
	vbox->addLayout(m_sliderGrid);
	
	QHBoxLayout *ctrlHBox = new QHBoxLayout();
	ctrlHBox->addStretch(1);
	
	m_showDisabled = new QCheckBox("Show Disabled Channels");
	m_showDisabled->setChecked(false);
	ctrlHBox->addWidget(m_showDisabled);
	
	connect(m_showDisabled, SIGNAL(toggled(bool)), this, SLOT(generateSliderUI()));
	
	vbox->addLayout(ctrlHBox);
	
	// Generate initial slider UI - will regenerate after changing channel indexes 
	generateSliderUI();
}
	
void OutputWindow::generateSliderUI()
{
	// Clear the slider grid of old controls
	while(m_sliderGrid->count() > 0)
	{
		QLayoutItem *item = m_sliderGrid->itemAt(m_sliderGrid->count() - 1);
		m_sliderGrid->removeItem(item);
		if(QWidget *widget = item->widget())
		{
			// disconnect any slots
			disconnect(widget, 0, this, 0);
			
			m_sliderGrid->removeWidget(widget);
			delete widget;
		}
			
		delete item;
		item = 0;
	}
	
	QWidget *parent = this;
	
	QGridLayout *lay = m_sliderGrid;
	
	// Rebuild UI
	QCheckBox *checkbox; // hold and enabled
	QSlider *slider; // value
	QLineEdit *edit; // name
	QSpinBox *spin; // value and channel
	QLabel *label;
	
	QList<QWidget*> wHold;
	QList<QWidget*> wValues;
	QList<QWidget*> wSliders;
	QList<QWidget*> wNums;
	QList<QWidget*> wNames;
	QList<QWidget*> wEnabled;
	
	bool showDisabled = m_showDisabled->isChecked();
	
	int row = 0;
	int col = 0;
	
	int counter = 0;
	
	// Sort m_channelList by channel num
	qSort(m_channelList.begin(), m_channelList.end(), SimpleChannel::comparitor);
	
	foreach(DmxChannel *chan, m_channelList)
	{
		row = 0;
		
		// Start a new widget hash
		WidgetHash widgets;
		
		if(!chan->isEnabled() && !showDisabled)
		{
			chan->setWidgets(widgets);
			counter++;
			continue;
		}
		
		checkbox = new QCheckBox("Hold");
		checkbox->setProperty("num",counter);
		checkbox->setChecked(chan->isHold());
		connect(checkbox, SIGNAL(toggled(bool)), this, SLOT(lockChannelChanged(bool)));
		lay->addWidget(checkbox, row++, col, Qt::AlignHCenter);
		wHold << checkbox;
		//widgets["hold"] = checkbox;
		
		IndicatorWidget *indicator = new IndicatorWidget(chan, parent);
		lay->addWidget(indicator, row++, col, Qt::AlignHCenter);
		
		spin = new QSpinBox();
		spin->setMinimum(0);
		spin->setMaximum(255);
		spin->setValue(chan->value());
		lay->addWidget(spin, row++, col, Qt::AlignHCenter);
		wValues << spin;
		widgets["spin"] = spin;
		
		slider = new QSlider(Qt::Vertical, parent);
		slider->setMinimum(0);
		slider->setMaximum(255);
		slider->setValue(chan->value());
		slider->setProperty("num",counter);
		connect(slider, SIGNAL(valueChanged(int)), parent, SLOT(sliderValueChanged(int)));
		connect(spin, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));
		connect(slider, SIGNAL(valueChanged(int)), spin, SLOT(setValue(int)));
		wSliders << slider;
		lay->addWidget(slider, row++, col, Qt::AlignHCenter);
		widgets["value"] = slider;
		
		spin = new QSpinBox();
		spin->setMinimum(0);
		spin->setMaximum(m_channelList.size());
		spin->setValue(chan->num());
		spin->setProperty("num",counter);
		connect(spin, SIGNAL(valueChanged(int)), this, SLOT(channelNumChanged(int)));
		lay->addWidget(spin, row++, col);
		wNums << spin;
		widgets["chan"] = spin;
		
		label = new QLabel(QString("DMX #%1").arg(chan->dmxNum()));
		lay->addWidget(label, row++, col);
		widgets["dmx"] = label;
			
		edit = new QLineEdit(chan->name());
		edit->setProperty("num",counter);
		connect(edit, SIGNAL(textChanged(QString)), this, SLOT(nameChanged(QString)));
		lay->addWidget(edit, row++, col);
		wNames << edit;
		widgets["name"] = edit;
		
		checkbox = new QCheckBox("Enab");
		checkbox->setProperty("num",counter);
		checkbox->setChecked(chan->isEnabled());
		connect(checkbox, SIGNAL(toggled(bool)), this, SLOT(enabledChanged(bool)));
		lay->addWidget(checkbox, row++, col);
		wHold << checkbox;
		widgets["enab"] = checkbox;
		
		// Store UI element pointers for updates later
		chan->setWidgets(widgets);
		
		updateHoldUi(chan);
		
		col++;
		counter++;
	}
	
	lay->addItem(new QSpacerItem(1,1,QSizePolicy::Expanding),1,col++);
	
	QWidget *lw = 0;
	QList<QWidget*> allWidgets  = QList<QWidget*>() 
		<< wHold
		<< wValues
		<< wSliders
		<< wNums
		<< wNames
		<< wEnabled;
	
	foreach(QWidget *w, allWidgets)
	{
		if(lw)
			QWidget::setTabOrder(lw,w);
		lw = w;
	}
	
	adjustSize();
}

#define GetChannelFromSender() \
	QObject *senderObject = sender(); \
	int channelNum = senderObject ? senderObject->property("num").toInt() : -1; \
	if(channelNum < 0) \
	{ \
		qDebug() << "Error reading slider number, nothing changed."; \
		return; \
	} \
	if(channelNum < 0 || channelNum >= m_channelList.size()) \
	{ \
		qDebug() << "Slider number out of range.";\
		return; \
	} \
	DmxChannel *chan = m_channelList[channelNum];


void OutputWindow::sliderValueChanged(int value)
{
	GetChannelFromSender();
	//qDebug() << "OutputWindow::sliderValueChanged: channelNum:"<<channelNum<<", dmxNum:"<<chan->dmxNum();
	chan->setValue(value);
}

void OutputWindow::nameChanged(QString name)
{
	GetChannelFromSender();
	chan->setName(name);
}

void OutputWindow::channelNumChanged(int num)
{
	GetChannelFromSender();
	
	// Swap numbers with the channel posessing the new number
// 	int oldNum = chan->num();
// 	chan->setNum(value);
// 	
// 	DmxChannel *oldChan = m_channelList[value-1];
// 	oldChan->setNum(oldNum);

	if(num<0)
		num = 0;
	if(num >= m_channelList.size())
		num = m_channelList.size()-1;
	
	if(chan->num() == num)
	{
		qDebug() << "OutputWindow::channelNumChanged: nums match, not changing";
		return;	
	}
	
	qDebug() << "OutputWindow::channelNumChanged: preset:"<<chan->name()<<", new num:"<<num;
		
	chan->setNum(num);
	
	m_channelList.removeAll(chan);
	m_channelList.insert(num, chan);
	
	for(int i=num+1;i<m_channelList.size();i++)
	{
		qDebug() << "OutputWindow::channelNumChanged: Renumbered "<<m_channelList[i]->name()<<" to "<<i;
		m_channelList[i]->setNum(i);
	}
	
	QTimer::singleShot(0, this, SLOT(generateSliderUI()));
}

bool OutputWindow::holdTransmit(bool flag)
{
	bool curValue = m_holdTransmit;
	
	if(flag)
	{
		foreach(DmxChannel *chan, m_channelList)
			chan->setHoldTransmit(false);
	}
	else
	{
		// - What?
		// We don't just turn off the holdTransmit flag, we also 
		// load current values, read our channels and set the 
		// value to current values, then transmit the entire
		// value list in one go.
		// - Why? This way, users (other classes) can call
		// holdTransmit(true) when they are going to be making
		// multiple changes, then holdTransmit(false) when done
		// and this way one big update gets transmitted.
		// - See example usage in mainLevelChanged(int) below
		
		DmxClient *client = MainWindow::inst()->dmxClient();
		DmxValueList channels = client->channelStates();
	
		// effectiveValue calculates the value with the mains value applied
		foreach(DmxChannel *chan, m_channelList)
			channels[chan->dmxNum()] = chan->effectiveValue();
	
		client->setDmxChannels(channels);
		
		foreach(DmxChannel *chan, m_channelList)
			chan->setHoldTransmit(false);
	}
		
	m_holdTransmit = flag;
	return curValue;
}

void OutputWindow::mainLevelChanged(int value)
{
	holdTransmit(true);
	
	foreach(DmxChannel *chan, m_channelList)
		chan->setMainValue(value);
		
	holdTransmit(false);

// 	qDebug() << "OutputWindow::mainLevelChanged: "<<value; 
// 	DmxClient *client = MainWindow::inst()->dmxClient();
// 	DmxValueList channels = client->channelStates();
// 	
// 	foreach(DmxChannel *chan, m_channelList)
// 	{
// 		chan->setHoldTransmit(true);
// 		chan->setMainValue(value);
// 		channels[chan->dmxNum()] = chan->effectiveValue();
// 	}
// 	
// 	client->setDmxChannels(channels);
// 	
// 	foreach(DmxChannel *chan, m_channelList)
// 		chan->setHoldTransmit(false);
}

void OutputWindow::enabledChanged(bool flag)
{
	GetChannelFromSender();
	chan->setIsEnabled(flag);
	
	if(!m_showDisabled->isChecked() && !flag)
		QTimer::singleShot(0, this, SLOT(generateSliderUI()));
}

void OutputWindow::lockChannelChanged(bool flag)
{
	GetChannelFromSender();
	chan->setIsHold(flag);
	updateHoldUi(chan);
}

void OutputWindow::updateHoldUi(DmxChannel *chan)
{
	WidgetHash widgets = chan->widgets();
	bool flag = chan->isHold();
	
	foreach(QWidget *widget, widgets.values())
		widget->setEnabled(!flag);
		
// 	widgets["value"]->setEnabled(!flag);
// 	widgets["spin"]->setEnabled(!flag);
// 	widgets["chan"]->setEnabled(!flag);
// 	widgets["name"]->setEnabled(!flag);
// 	widgets["enab"]->setEnabled(!flag);
// 	widgets["dmx"]->setEnabled(!flag);
}

void OutputWindow::dmxChannelValueChanged(int val)
{
	DmxChannel *chan = dynamic_cast<DmxChannel*>(sender());
	if(!chan)
	{
		qDebug() << "Error finding channel when dmx channel value changed";
		return;
	}
	
	WidgetHash widgets = chan->widgets();
	QSlider *slider = dynamic_cast<QSlider*>(widgets["value"]);
	if(!slider)
	{
		//qDebug() << "Error finding slider for channel "<<chan->num()<<" "<<chan->name()<<", val:"<<val;
		return;
	}
	
	if(slider->value() != val)
		slider->setValue(val);
}

